#include <cmath>
#include <iostream>
#include <vector>
#include "RayTracer.h"
#include "Ray.h"
#include "Shapes.h"
#include "Color.h"
#include "Transform.h"
#include "Orthographic.h" 

using std::vector;
using MathGraphics::Ray;
using MathGraphics::Vector;
using MathGraphics::Point;
using MathGraphics::Transform;
using GraphicsColor::RGB;
using CameraInterface::OrthoCamera;

RayTracerInterface::RayTracer::RayTracer(Scene* s, Renderer::SDLRenderer* r)
{
	scene = s;
	render = r;
}

void RayTracerInterface::RayTracer::rayTrace()
{
	vector<vector<int>> film(1680, vector<int>(1050, 0));

	Transform worldToCamera = lookAt(Point(0.0, 0.0, 0.0), Point(0.0, 0.0, 1.0), Vector(0.0, -1.0, 0.0));
	OrthoCamera camera(scene->w, scene->h, 1.0, 1000.0, worldToCamera);
	Ray r(Point(0, 0, 0), Vector(0, 0, 1.0f));

	for (int i = 0; i < scene->w; i++)
	{
		for (int j = 0; j < scene->h; j++)
		{
			double red   = 0.0;
			double green = 0.0;
			double blue  = 0.0;
			r = camera.spawnRay(i, j);

			//for (double fragmentx = i; fragmentx < i + 1.0f; fragmentx += 0.5f)
			//{
				//for (double fragmenty = j; fragmenty < j + 1.0f; fragmenty += 0.5f)
				//{

					int level = 0; //reflection depth
					double coef = 1.0;

					//camera(r, &r);
					//r.o.x *= -1;
					//r.v.x = i - r.o.x; //fragmentx - 840.0f;
					//r.v.y = j - r.o.y; //fragmenty - 525.0f;
					//r.v.z = 3300.0f - r.o.z; 

					if (i == 840 && j == 525)
						bool stop = true;

					r.v = r.v.normalize();

					Vector nir;
					do
					{
						//find nearest intersection 
						int nearo = -1;

						for (::Scene::vssize k = 0; k < scene->objects.size(); k++)
						{
							if (scene->objects[k]->intersect(r))
							{
								nearo = k;
							}
						}
						

						if (nearo == -1)
						{
							break;
						}

						//holds point of intersection of ray
						Vector temp = r.t * r.v;
						nir.x = r.o.x + temp.x;
						nir.y = r.o.y + temp.y;
						nir.z = r.o.z + temp.z;

						//calculate normal
						Vector n = scene->objects[nearo]->getNormal(nir);

						//Calculate dot product of point of intersection and light
						Vector dota(0.0f, 0.0f, 0.0f);
						for (::Scene::vssize k = 0; k < scene->lights.size(); k++)
						{
							dota = scene->lights[k]->getNormal(Vector(0.0, 0.0, 0.0)) - nir;
							//dota = (*(scene->lights[k]->c)) - nir;
							dota = dota.normalize();
						}
						double shade = dota * n;

						//Dont shade anything that is not facing light
						if (shade < 0.0f)
						{
							shade = 0.0f;
						}
						else 
						{
							Ray ri(Point(nir.x, nir.y, nir.z), Vector(0, 0, 0));
							Ray rj(Point(nir.x, nir.y, nir.z), Vector(0, 0, 0));

							for (::Scene::vssize k = 0; k < scene->lights.size(); k++)
							{
								Vector p = scene->lights[k]->getNormal(Vector(0.0, 0.0, 0.0)) - nir;
								//Vector p = (*(scene->lights[k]->c)) - nir;
								p = p.normalize();

								ri.v = Vector(&p);
								rj.v = Vector(&p);

								scene->lights[k]->intersect(rj); 

								for (::Scene::vssize m = 0; m < scene->objects.size(); m++)
								{
									ri.t = 10000.0f; 

									//if ray intersects an object before a light then object is in shadow
									if (scene->objects[m]->intersect(ri) && ri.t < rj.t)
									{
										shade = 0.0f;
										break;
									}
								}
							}
						}

						red   += scene->objects[nearo]->col->r * (0.1f + (shade * .9f)) * 1.0f * coef; //* 0.25f;
						green += scene->objects[nearo]->col->g * (0.1f + (shade * .9f)) * 1.0f * coef; //* 0.25f;
						blue  += scene->objects[nearo]->col->b * (0.1f + (shade * .9f)) * 1.0f * coef; //* 0.25f;

						if (red > 255.0)
						{
							red = 255;
						}
						if (green > 255.0)
						{
							green = 255;
						}
						if (blue > 255.0)
						{
							blue = 255.0;
						}

						coef *= scene->objects[nearo]->material.reflect;
						double reflect = 2.0f * (r.v * n);
						r.o.x = nir.x;
						r.o.y = nir.y;
						r.o.z = nir.z;

						r.v = r.v - (reflect * n);
						r.t = 10000.0f;

						level++;
					} while(level < 100 && coef > 0.0);

					//reset ray parameter
					r.t = 10000.0f;
				//}
			//}
			film[i][j] = RGB::getHex(red, green, blue);
			//render->drawPixel(i, j, RGB::getHex(red, green, blue));
		}
		//std::cout<<"Percent Done: "<<(double)i / (double)scene->w<<std::endl;
	}
	render->drawPixel(film);

	render->render();
}